package com.ezops.poc.parser;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ezops.poc.bo.AssetLookUp;
import com.ezops.poc.bo.ClientBaseTradeFileModel;
import com.ezops.poc.bo.ClientLookUp;
import com.ezops.poc.util.ClientModelFactory;
import com.ezops.poc.util.EzopsParseException;

/**
 * This is a custom converter class which will transform the data (in format @code
 * (List<List<String>>)) into the client format pojo
 * 
 * TODO : We can have a factory of these converters in future for converting the
 * data into different set of pojo or make a dynamic model.
 * 
 * @author vgaurav
 * 
 */

@Component(value="clientFormatParser")
public class ClientFormatParser implements Processor {
	
	@Autowired
	private AssetLookUp assetLookUp;
	
	@Autowired
	private ClientLookUp clientLookUp;

	// CLIENTNAME_ASSETTYPE_NAME.csv
	private final static Pattern FILENAME_PATTERN = Pattern
			.compile("(.*?)_(.*?)_(.*?)\\..*");
	
	/**
	 * Process the incoming data, parse and convert it into model and send it
	 * further down the pipe.
	 * @throws EzopsParseException
	 */

	public void process(Exchange exchange) throws EzopsParseException {

		String fileName = (String) exchange.getIn().getHeader(
				Exchange.FILE_NAME_CONSUMED);

		Matcher m = FILENAME_PATTERN.matcher(fileName);
		if (!m.matches()) {
			throw new IllegalArgumentException(
					"Filename doesn't match expected format: " + fileName);
		}
		String assetId = m.group(2);
		String clientId = m.group(1);

		//TODO : Need to be changed in future.
		Class modelClass = ClientModelFactory.getType(clientId, assetId);

		@SuppressWarnings({ "unchecked", "unused" })
		List<List<String>> body = (List<List<String>>) exchange.getIn()
				.getBody();
		String[] header = extractHeader(body);
		List<List<String>> data = extractData(body);

		try {
			// Set the parsed list of model into exchange body for further
			// processing.
			exchange.getIn()
					.setBody(
							parse(modelClass, header, data, fileName, assetId,
									clientId));
		} catch (Exception e) {
			e.printStackTrace();
			throw new EzopsParseException(fileName, e,
					"Couldn't parse the file");
		}

	}

	
	/**
	 * Set the data to be parsed. First row is considered as a header.
	 * @param incomingData
	 * @return
	 */
	private List<List<String>> extractData(List<List<String>> incomingData) {

		if (incomingData == null || incomingData.isEmpty()) {
			throw new IllegalArgumentException("File is empty");
		}
		// remove header
		incomingData.remove(0);
		return incomingData;
	}

	/**
	 * Validate if the headers are correct. Will be implemented in future.
	 * @param headers
	 * @return
	 */
	private boolean validateHeaders(List<String> headers) {
		return true;

	}

	/**
	 * Return the headers.
	 * 
	 * @param columns
	 */
	private String[] extractHeader(List<List<String>> incomingData) {
		if (incomingData == null || incomingData.size() == 0) {
			throw new IllegalArgumentException("Header can not be empty");
		}
		List<String> headers = incomingData.get(0);
		validateHeaders(headers);
		return headers.toArray(new String[headers.size()]);
	}

	/**
	 * Parse the @param data into @param classType object using reflection.
	 * @param classType
	 * @param header
	 * @param data
	 * @param fileName
	 * @param assetId
	 * @param clientId
	 * @return
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private <T> List<T> parse(Class classType, String[] header,
			List<List<String>> data, String fileName, String assetId,
			String clientId) throws NoSuchFieldException, SecurityException,
			IllegalArgumentException, IllegalAccessException,
			InstantiationException {

		List<T> convertedList = new ArrayList<T>();

		for (List<String> row : data) {
			T instance = (T) classType.newInstance();
			Iterator<String> dataIterator = row.iterator();

			// transform row into pojo.
			for (String field : header) {
				String pojoField = Character.toLowerCase(field.charAt(0))
						+ field.substring(1, field.length());
				pojoField = pojoField.replace(".", "").replace(" ", "");
				Field reflectiveField = classType.getDeclaredField(pojoField);
				reflectiveField.setAccessible(true);
				reflectiveField.set(instance, dataIterator.next());
			}

			// set file name, assetid and clientId.
			if (instance instanceof ClientBaseTradeFileModel) {
				ClientBaseTradeFileModel baseModel = (ClientBaseTradeFileModel) instance;
				baseModel.setFileName(fileName);
				baseModel.setClientId(clientLookUp.lookUp(clientId));
				baseModel.setAssetId(assetLookUp.lookUp(assetId));
			}
			convertedList.add(instance);
		}
		return convertedList;
	}
}
